| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259 |
- /*
- CocosCreater 3.8.x 2d曲线纹理插件
- 可用来制作2d横版的地形,如:山脉,道路等,类似登山赛车和滑雪大冒险的横版平滑的地图
- CocosCreater 3.8.x 2d curve texture plugin
- Can be used to make 2d side-scrolling terrain, such as: mountains, roads, etc., similar to the smooth maps in Hill Climb Racing and Ski Safari
- 作者:soida
- Author: soida
- 微信:soida3
- WeChat: soida3
- Cocos商店(https://store.cocos.com)也有出售,可直接搜索soida或者关键词'曲线纹理'
- Also available in the Cocos store(https://store.cocos.com), just search the keyword 'soida' '曲线纹理' 'curve texture'
- */
- // import { EDITOR, EDITOR_NOT_IN_PREVIEW } from 'cc/env';
- import {
- _decorator,
- Asset,
- AssetManager,
- CCBoolean,
- CCFloat,
- CCInteger,
- Color,
- Component,
- EffectAsset,
- Enum,
- ERigidBody2DType,
- EventKeyboard,
- gfx,
- Input,
- input,
- KeyCode,
- Mat4,
- Material,
- Mesh,
- MeshRenderer,
- misc,
- Node,
- PolygonCollider2D,
- RenderTexture,
- resources,
- RigidBody2D,
- Sprite,
- SpriteFrame,
- sys,
- Texture2D,
- TransformBit,
- tween,
- Tween,
- UIMeshRenderer,
- UIOpacity,
- UITransform,
- utils,
- v2,
- v3,
- v4,
- Vec2,
- Vec3,
- } from 'cc';
- import { EDITOR, EDITOR_NOT_IN_PREVIEW } from 'cc/env';
- import { MoveWithTouch } from './MoveWithTouch';
- import { UsefulTools } from './UsefulTools';
- import { MeshSprite } from './MeshSprite';
- const { ccclass, property, executeInEditMode, requireComponent, disallowMultiple, menu, help } = _decorator;
- function getGroupStyleName() {
- if (EDITOR) {
- //@ts-ignore
- const v = Editor.App.version;
- return UsefulTools.compareVersions(v, '3.8.0') >= 0 ? 'section' : 'tab';
- }
- return 'tab';
- }
- const GROUP_STYLE_NAME: string = getGroupStyleName();
- //兼容低版本未定义
- if (EDITOR_NOT_IN_PREVIEW === undefined) {
- console.warn('当前环境无 EDITOR_NOT_IN_PREVIEW 定义,开始兼容性定义');
- const EDITOR_NOT_IN_PREVIEW = EDITOR;
- }
- //判断Vec2类的原型是否有toVec3()方法,如果没有就添加一个
- //@ts-ignore
- if (!Vec2.prototype.toVec3) {
- console.warn('当前环境无 Vec2.toVec3() 定义,开始兼容性定义');
- //@ts-ignore
- Vec2.prototype.toVec3 = function (this: Vec2, z: number = 0): Vec3 {
- return new Vec3(this.x, this.y, z);
- };
- }
- //判断Vec3类的原型是否有toVec2()方法,如果没有就添加一个
- //@ts-ignore
- if (!Vec3.prototype.toVec2) {
- console.warn('当前环境无 Vec3.toVec2() 定义,开始兼容性定义');
- //@ts-ignore
- Vec3.prototype.toVec2 = function (this: Vec3): Vec2 {
- return new Vec2(this.x, this.y);
- };
- }
- /**
- *
- * @returns 是否编辑器中文环境,非运行环境 'Whether the editor is in Chinese environment, not running environment'
- */
- export function inEditorZH(): boolean {
- if (EDITOR) {
- //@ts-ignore
- return Editor.I18n.getLanguage() === 'zh';
- }
- return false;
- }
- const IN_EDITOR_ZH: boolean = inEditorZH();
- function isCloseToVec3(self: Vec3, other: Vec3, epsilon = 0.00001): boolean {
- const dx = self.x - other.x;
- const dy = self.y - other.y;
- const dz = self.z - other.z;
- const squaredDistance = dx * dx + dy * dy + dz * dz;
- return squaredDistance <= epsilon * epsilon;
- }
- function isCloseToVec2(self: Vec2, other: Vec2, epsilon = 0.00001): boolean {
- const dx = self.x - other.x;
- const dy = self.y - other.y;
- const squaredDistance = dx * dx + dy * dy;
- return squaredDistance <= epsilon * epsilon;
- }
- export enum TerrainRenderMode {
- VERTICAL_MODE = 0, //正常竖直模式,按照控制点竖直向下渲染
- TANGENT_MODE = 1, //切线模式,按照控制点的切线垂直方向向下渲染
- TANGENT_MODE_CENTER = 2, //切线模式,按照控制点在中心点,向上下各渲染一半的纹理高度
- }
- export enum TerrainRenderUVMode {
- UV_REPEAT_GRID = 0, //uv计算按照默认网格重复
- UV_REPEAT_LENGTH = 1, //uv计算按照长度重复
- }
- @ccclass('CurveTexture')
- @requireComponent(MeshRenderer)
- @disallowMultiple
- @executeInEditMode
- @help('https://github.com/soidaken/CurveTexture2d')
- @menu('2D/CurveTexture2d(曲线纹理2d)')
- export class CurveTexture extends Component {
- private _curve_width: number = 512;
- private _curve_height: number = 512;
- private _curve_height_result: number = 512;
- private _curve_collider_thickness: number = 0;
- // private _isZH: boolean = sys.language == 'zh';
- private _headNode: Node | null = null;
- private _tailNode: Node | null = null;
- @property({})
- private _syncOnOff: boolean = false;
- @property({
- tooltip: IN_EDITOR_ZH
- ? '自动同步另一个节点的地形数据,开启后此节点无法编辑地形'
- : ' (Automatically sync terrain data from another node, this node cannot edit terrain when enabled)',
- displayName: IN_EDITOR_ZH ? '开启跟随模式' : ' Enable Follow Mode',
- })
- get syncOnOff() {
- return this._syncOnOff;
- }
- set syncOnOff(value) {
- if (!value) {
- this.syncTarget = null;
- }
- this._syncOnOff = value;
- this._refreshAll();
- }
- @property({ type: Node })
- private _syncTarget: Node | null = null;
- @property({
- type: Node,
- tooltip: IN_EDITOR_ZH ? '跟随的目标节点' : ' (Target node to follow)',
- displayName: IN_EDITOR_ZH ? '跟随目标' : ' (Follow Target)',
- // visible() {
- // return this._syncOnOff;
- // },
- })
- get syncTarget() {
- return this._syncTarget;
- }
- set syncTarget(value) {
- if (this._syncOnOff) {
- if (value) {
- let c = value.getComponent(CurveTexture);
- if (!c) {
- console.warn(
- IN_EDITOR_ZH
- ? '跟随目标节点没有CurveTexture组件,请添加'
- : ' (The target node to follow does not have a CurveTexture component, please add it)'
- );
- return;
- }
- if (value === this.node) {
- console.warn(IN_EDITOR_ZH ? '跟随目标节点不能是自己' : ' (The target node to follow cannot be itself)');
- return;
- }
- //防止循环跟随 'Prevent circular following'
- if (c.syncTarget) {
- if (c.syncTarget === this.node) {
- console.warn(
- IN_EDITOR_ZH ? '出现循环跟随啦,非法操作!' : ' (Circular following detected, illegal operation!)'
- );
- return;
- }
- }
- }
- this._syncTarget = value;
- this._refreshAll();
- } else {
- console.warn(IN_EDITOR_ZH ? '需要先开启跟随模式' : ' (Follow mode needs to be enabled first)');
- }
- }
- @property({ type: Enum(TerrainRenderMode) })
- private _renderMode: TerrainRenderMode = TerrainRenderMode.VERTICAL_MODE;
- @property({
- type: Enum(TerrainRenderMode),
- tooltip: IN_EDITOR_ZH
- ? '渲染顶点模式,VERTICAL_MODE为按照控制点竖直向下渲染,适合厚纹理;TANGENT_MODE/TANGENT_MODE_CENTER为按照控制点的切线垂直方向向下渲染,适合细纹理'
- : ' (Terrain rendering mode, VERTICAL_MODE renders vertically downward according to control points, suitable for thick textures; TANGENT_MODE/TANGENT_MODE_CENTER renders vertically downward according to the tangent direction of control points, suitable for thin textures)',
- displayName: IN_EDITOR_ZH ? '渲染顶点模式' : ' Terrain Rendering Mod',
- group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
- })
- get renderMode() {
- return this._renderMode;
- }
- set renderMode(value) {
- this._renderMode = value;
- // if (value == TerrainRenderMode.TANGENT_MODE || value == TerrainRenderMode.TANGENT_MODE_CENTER) {
- // this._thickness = 0;
- // }
- this._refreshAll();
- }
- @property({ type: Enum(TerrainRenderUVMode) })
- private _renderUVMode: TerrainRenderUVMode = TerrainRenderUVMode.UV_REPEAT_GRID;
- @property({
- type: Enum(TerrainRenderUVMode),
- tooltip: IN_EDITOR_ZH
- ? '渲染UV模式,UV_REPEAT_GRID为按照默认网格重复;UV_REPEAT_LENGTH为按照长度重复'
- : ' (Terrain UV rendering mode, UV_REPEAT_GRID repeats according to the default grid; UV_REPEAT_LENGTH repeats according to length)',
- displayName: IN_EDITOR_ZH ? '渲染UV模式' : ' Terrain Rendering UV Mod',
- group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
- })
- get renderUVMode() {
- return this._renderUVMode;
- }
- set renderUVMode(value) {
- this._renderUVMode = value;
- // if (value == TerrainRenderMode.TANGENT_MODE || value == TerrainRenderMode.TANGENT_MODE_CENTER) {
- // this._thickness = 0;
- // }
- this._refreshAll();
- }
- @property({ type: EffectAsset })
- private _spEffectAsset: EffectAsset | null = null;
- @property({
- type: EffectAsset,
- tooltip: IN_EDITOR_ZH ? '渲染effect文件' : 'effect asset',
- displayName: IN_EDITOR_ZH ? '渲染effect文件' : 'effect asset',
- group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
- })
- get spEffectAsset() {
- return this._spEffectAsset;
- }
- set spEffectAsset(value) {
- this._spEffectAsset = value;
- this._refreshAll();
- }
- private _gizmoInitColor: Color = new Color(255, 255, 255, 255);
- @property({ type: SpriteFrame })
- private _spFrame: SpriteFrame | null = null;
- @property({
- type: SpriteFrame,
- tooltip: IN_EDITOR_ZH ? '渲染纹理' : ' Terrain texture',
- displayName: IN_EDITOR_ZH ? '渲染纹理' : ' Terrain Texture',
- group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
- })
- get spFrame() {
- return this._spFrame;
- }
- set spFrame(value) {
- this._spFrame = value;
- //取纹理一片区域的颜色均值作为gizmo的颜色
- const uint8arr = RenderTexture.prototype.readPixels.call(value.texture, 0, this._spFrame.height / 2 - 2, 4, 4);
- let r = 0,
- g = 0,
- b = 0;
- for (let i = 0; i < uint8arr.length; i += 4) {
- r += uint8arr[i];
- g += uint8arr[i + 1];
- b += uint8arr[i + 2];
- //忽略alpha通道
- }
- r /= 16;
- g /= 16;
- b /= 16;
- // 将rgb值调整为同色系并提升亮度两个等级
- let [h, s, l] = UsefulTools.rgbToHsl(r, g, b);
- // 提升亮度两个等级(每级约0.08,提升0.16,最大不超过0.95)
- l = Math.min(l + 0.16, 0.95);
- [r, g, b] = UsefulTools.hslToRgb(h, s, l);
- this._gizmoInitColor.set(r, g, b, this.gizmoColor.a);
- // console.log('this._gizmoInitColor ', this._gizmoInitColor);
- this.gizmoColor = this._gizmoInitColor;
- this._refreshAll();
- }
- @property({})
- private _headTailFixedOnOff: boolean = false;
- @property({
- tooltip: IN_EDITOR_ZH ? '首尾是否有固定纹理' : 'head/tail fixed texture',
- displayName: IN_EDITOR_ZH ? '首尾是否有固定纹理' : 'head/tail fixed texture',
- group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
- visible(this: CurveTexture) {
- return (
- this._renderMode == TerrainRenderMode.TANGENT_MODE || this._renderMode == TerrainRenderMode.TANGENT_MODE_CENTER
- );
- },
- })
- get headTailFixedOnOff() {
- return this._headTailFixedOnOff;
- }
- set headTailFixedOnOff(value) {
- this._headTailFixedOnOff = value;
- this._refreshAll();
- }
- @property({ type: SpriteFrame })
- private _headTailFrame: SpriteFrame | null = null;
- @property({
- type: SpriteFrame,
- tooltip: IN_EDITOR_ZH ? '首尾纹理' : ' Terrain texture',
- displayName: IN_EDITOR_ZH ? '首尾纹理' : ' Terrain Texture',
- group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
- visible(this: CurveTexture) {
- return this._headTailFixedOnOff == true;
- },
- })
- get headTailFrame() {
- return this._headTailFrame;
- }
- set headTailFrame(value) {
- this._headTailFrame = value;
- this._refreshAll();
- }
- // @property({})
- // private _alphaThreshold: number = 0.4;
- // @property({
- // tooltip: IN_EDITOR_ZH
- // ? '如果纹理有透明渲染异常,可以调整这里'
- // : ' (If there is a transparent rendering issue with the texture, you can adjust here)',
- // displayName: IN_EDITOR_ZH ? '如果纹理有透明' : ' If the texture has transparency',
- // group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
- // slide: true,
- // range: [0, 1, 0.01],
- // })
- // get alphaThreshold() {
- // return this._alphaThreshold;
- // }
- // set alphaThreshold(value) {
- // this._alphaThreshold = value;
- // this._refreshAll();
- // }
- @property({})
- private _updownFix: boolean = false;
- @property({
- tooltip: IN_EDITOR_ZH ? '如果上下反了,就来点点我!' : ' (If it is upside down, click me!)',
- displayName: IN_EDITOR_ZH ? '上下反了?' : ' (Upside Down?)',
- group: IN_EDITOR_ZH ? '渲染属性' : ' (Terrain Rendering)',
- })
- get updownFix() {
- return this._updownFix;
- }
- set updownFix(value) {
- this._updownFix = value;
- this._refreshAll();
- }
- @property({})
- private _thickness: number = 0;
- @property({
- tooltip: IN_EDITOR_ZH
- ? '地形的厚度,填0是设置为纹理高度值'
- : ' (Thickness of the terrain, 0 sets it to the texture height value)',
- displayName: IN_EDITOR_ZH ? '渲染厚度' : ' (Terrain Thickness)',
- group: IN_EDITOR_ZH ? '渲染属性' : ' (Terrain Rendering)',
- // visible(this: CurveTexture) {
- // return this._renderMode == TerrainRenderMode.VERTICAL_MODE;
- // },
- })
- get thickness() {
- return this._thickness;
- }
- set thickness(value) {
- this._thickness = value;
- this._refreshAll();
- }
- @property({})
- private _offset: Vec3 = v3(0, 0, 0);
- @property({
- type: Vec3,
- tooltip: IN_EDITOR_ZH
- ? '渲染起始的偏移值,z暂时无效'
- : ' (Offset value for rendering start, z is temporarily invalid)',
- group: IN_EDITOR_ZH ? '渲染属性' : ' (Terrain Rendering)',
- displayName: IN_EDITOR_ZH ? '渲染偏移' : ' (Rendering Offset)',
- })
- get offset() {
- return this._offset;
- }
- set offset(value) {
- this._offset = value;
- this._offset.z = 0;
- this._refreshAll();
- }
- // @property({})
- private _controlPoints: Vec3[] = [v3(-500, -130, 0), v3(-160, 30, 0), v3(160, -130, 0), v3(500, 30, 0)];
- get controlPoints() {
- return this._controlPoints;
- }
- set controlPoints(value) {
- this._controlPoints = null;
- this._controlPoints = value.slice();
- this._refreshAll();
- }
- @property({})
- private _smoothness: number = 16;
- @property({
- type: CCInteger,
- tooltip: IN_EDITOR_ZH
- ? '每两控制点之间细分的数量,默认16,如果是0,则不细分'
- : ' (Number of subdivisions between every two control points, default is 16)',
- displayName: IN_EDITOR_ZH ? '细分点数量' : ' (Number of Subdivided Points)',
- group: IN_EDITOR_ZH ? '渲染属性' : ' (Terrain Rendering)',
- slide: true,
- range: [0, 32, 2],
- })
- get smoothness() {
- return this._smoothness;
- }
- set smoothness(value) {
- this._smoothness = value % 2 == 1 ? value + 1 : value;
- this._refreshAll();
- }
- @property({})
- private _headTailCloseOnoff: boolean = false;
- @property({
- tooltip: IN_EDITOR_ZH
- ? '是否首尾闭合,渲染为一个闭合的区域'
- : ' (Whether the head and tail are closed, rendered as a closed area)',
- displayName: IN_EDITOR_ZH ? '是否首尾闭合' : ' (Head and Tail Closure)',
- group: IN_EDITOR_ZH ? '首尾闭合属性' : ' (Head and Tail Closure Parameters)',
- })
- get headTailCloseOnoff() {
- return this._headTailCloseOnoff;
- }
- set headTailCloseOnoff(value) {
- if (!value) {
- this._headTailCloseOnoff = false;
- this._refreshAll();
- return;
- }
- //检测闭合后的顶点组成的多边形是否符合规则,1:不能自交 2:不能有连续共线点 3:不能有重复点
- //先检测控制点,符合规则后看有无必要再检测细分后的点
- const result2 = UsefulTools.isPolygonSelfIntersect(this._controlPoints);
- if (result2.result) {
- // console.log('length ', this._editNodes.length);
- if (this._editNodes) {
- const n1 = this._editNodes[result2.index];
- const n2 = this._editNodes[result2.index + 1];
- n1.setScale(1.2, 1.2, 1);
- setTimeout(() => {
- n1.setScale(1, 1, 1);
- }, 250);
- n2.setScale(1.2, 1.2, 1);
- setTimeout(() => {
- n2.setScale(1, 1, 1);
- }, 250);
- }
- console.warn(
- IN_EDITOR_ZH
- ? `${this.node.name} 控制点有自相交,请检查! `
- : ' (Control points have self-intersection, please check!)'
- );
- return;
- }
- const result1 = UsefulTools.isCollinear(this._controlPoints);
- if (result1.result) {
- console.warn(
- IN_EDITOR_ZH
- ? `${this.node.name} 控制点有连续共线点,请检查! 点索引:${result1.index}/${result1.index + 1}/${
- result1.index + 2
- }`
- : ' (Control points have continuous collinear points, please check!)'
- );
- return;
- }
- if (UsefulTools.hasDuplicatePoints(this._controlPoints)) {
- console.warn(
- IN_EDITOR_ZH
- ? `${this.node.name} 控制点有重复点,请检查!`
- : ' (Control points have duplicate points, please check!)'
- );
- return;
- }
- this._headTailCloseOnoff = value;
- this._refreshAll();
- }
- @property({ type: MeshSprite })
- private _innerMeshSprite: MeshSprite | null = null;
- @property({
- tooltip: IN_EDITOR_ZH
- ? '闭合后内部渲染组件,如果不需要可以不设置'
- : ' (Inner rendering component after closure, if not needed, it can be left unset)',
- displayName: IN_EDITOR_ZH ? '闭合后内部渲染组件' : ' (Head and Tail Closure)',
- group: IN_EDITOR_ZH ? '首尾闭合属性' : ' (Head and Tail Closure Parameters)',
- type: MeshSprite,
- visible(this: CurveTexture) {
- return this._headTailCloseOnoff;
- },
- })
- get innerMeshSprite(): MeshSprite {
- return this._innerMeshSprite;
- }
- set innerMeshSprite(value: MeshSprite) {
- this._innerMeshSprite = value;
- this._refreshAll();
- }
- @property({})
- private _physcisOnOff: boolean = false;
- @property({
- tooltip: IN_EDITOR_ZH
- ? '在一个特定子节点上添加[PhysicsPolygonCollider]组件,你也可以通过[getCurveTexturePoints] 获取数据后自己实现.'
- : 'Add a [PhysicsPolygonCollider] component to a specific child node, or you can implement it yourself after fetching data from [getCurveTexturePoints].)',
- displayName: IN_EDITOR_ZH ? '同步生成物理属性' : ' (Synchronize Generation of Physical Properties)',
- group: IN_EDITOR_ZH ? '物理属性' : ' (Physical Properties)',
- })
- get physcisOnOff() {
- return this._physcisOnOff;
- }
- set physcisOnOff(value) {
- this._physcisOnOff = value;
- this._refreshAll();
- }
- @property({})
- private _physicsThickness: number = 0;
- @property({
- type: CCFloat,
- tooltip: IN_EDITOR_ZH
- ? '物理碰撞器的厚度,填0是设置为纹理高度值'
- : ' (Thickness of the physical collider, 0 sets it to the texture height value)',
- displayName: IN_EDITOR_ZH ? '物理碰撞器厚度' : ' (Physical Collider Thickness)',
- group: IN_EDITOR_ZH ? '物理属性' : ' (Physical Properties)',
- visible(this: CurveTexture) {
- return this._physcisOnOff;
- },
- })
- get physicsThickness() {
- return this._physicsThickness;
- }
- set physicsThickness(value) {
- this._physicsThickness = value;
- this._refreshAll();
- }
- private _physicsCollider: PolygonCollider2D | null = null;
- //获取对应的物理碰撞器 'Get the corresponding physical collider'
- public get physicsCollider() {
- return this._physicsCollider;
- }
- @property({})
- private _offsetCollider: Vec3 = v3(0, 0, 0);
- @property({
- type: Vec3,
- tooltip: IN_EDITOR_ZH
- ? '物理碰撞器的偏移值,默认(0,0,0)'
- : ' (Offset value for the physical collider, default is (0,0,0))',
- displayName: IN_EDITOR_ZH ? '物理碰撞器偏移' : ' (Physical Collider Offset)',
- group: IN_EDITOR_ZH ? '物理属性' : ' (Physical Properties)',
- visible(this: CurveTexture) {
- return this._physcisOnOff;
- },
- })
- get offsetCollider() {
- return this._offsetCollider;
- }
- set offsetCollider(value) {
- this._offsetCollider = value;
- this._offsetCollider.z = 0;
- this._refreshAll();
- // if (this._physicsCollider) {
- // this._physicsCollider.offset = this._offsetCollider.clone();
- // }
- }
- /**
- *
- * @returns 获取细分后的所有数据点,相对于当前节点坐标系 'Get all subdivided data points, relative to the current node coordinate system'
- */
- public getCurveTexturePoints(): Vec3[] {
- return this._vertexesSegment;
- }
- //
- // private _chainCollider: PhysicsChainCollider = null;
- //将编辑点 _controlPoints 细分后的平滑渲染点 'Smooth rendering points after subdividing the editing points _controlPoints'
- private _vertexesSegment: Vec3[] = [];
- /**
- * 获取细分后的所有数据点,相对于当前节点坐标系 'Get all subdivided data points, relative to the current node coordinate system'
- */
- get vertexesSegment() {
- return this._vertexesSegment;
- }
- //最终的渲染顶点数据 'Final rendering vertex data'
- private _vertexes: Vec3[] = [];
- // get vertexes() {
- // return this._vertexes;
- // }
- // set vertexes(value) {
- // this._controlPoints = value.slice();
- // this._refreshAll();
- // }
- private renderer: MeshRenderer | null = null;
- protected onLoad() {
- let tmp: Vec3[] = [];
- let snodes: Node[] = [];
- for (let i = 0; i < this.node.children.length; i++) {
- const item = this.node.children[i];
- if (item.name.startsWith('gizmo_')) {
- snodes.push(item);
- }
- }
- if (this._sortInXDirection) {
- snodes.sort((a, b) => a.x - b.x);
- snodes.forEach((item, index) => {
- item.setSiblingIndex(index + 10);
- });
- }
- for (let i = 0; i < snodes.length; i++) {
- const item = snodes[i];
- tmp.push(item.position);
- }
- this._controlPoints = tmp.length > 0 ? tmp : this._controlPoints;
- }
- /**
- * 打印调试信息 'Print debug information'
- * _curve_width: 纹理宽度 'Texture width'
- * _curve_height: 纹理高度 'Texture height'
- * _controlPoints: 编辑点 'Editing points'
- * _vertexesSegment: 细分点 'Subdivided points'
- * _vertexes: 渲染点 'Rendering points'
- */
- debugPrint() {
- console.log(this.node.name + ' CurveTexture.ts debugPrint _curve_width : ', this._curve_width);
- console.log(this.node.name + ' CurveTexture.ts debugPrint _curve_height : ', this._curve_height);
- console.log(this.node.name + ' CurveTexture.ts debugPrint _controlPoints : ', this._controlPoints);
- console.log(this.node.name + ' CurveTexture.ts debugPrint _vertexesSegment : ', this._vertexesSegment);
- console.log(this.node.name + ' CurveTexture.ts debugPrint _vertexes : ', this._vertexes);
- }
- protected onEnable() {
- this.node.off(Node.EventType.TRANSFORM_CHANGED);
- this.node.on(
- Node.EventType.TRANSFORM_CHANGED,
- (type: TransformBit) => {
- if (type & Node.TransformBit.TRS) {
- // console.log('CurveTexture.ts TRS changed, 刷新一次' + ' (refresh once)');
- this._refreshAll('559');
- }
- },
- this
- );
- this._registerKeyForGizmo();
- this._refreshAll('565');
- }
- private _refreshAll(from: string = '') {
- // console.log(`from ${this.node.name} ${from}`);
- let renderer = this.node.getComponent(MeshRenderer);
- if (!renderer) {
- console.warn('MeshRenderer component not found!');
- return;
- }
- this.renderer = renderer;
- //强制转换2d渲染模式,使用2d的层级管理
- let uiMeshR = this.node.getComponent(UIMeshRenderer);
- if (!uiMeshR) {
- this.node.addComponent(UIMeshRenderer);
- }
- if (!this.renderer) {
- console.warn(
- IN_EDITOR_ZH
- ? '没有MeshRenderer组件,请添加MeshRenderer组件!'
- : ' (No MeshRenderer component, please add MeshRenderer component!)'
- );
- return;
- }
- let csame = this.node.getComponents(CurveTexture);
- if (csame.length > 1) {
- console.error(
- this.node.name + IN_EDITOR_ZH
- ? ' 请去除无用的[ CurveTexture ]组件,一个节点只需绑定一个,多个存在只会有最后一个生效!'
- : ' (Please remove unnecessary [CurveTexture] components, only one needs to be bound to a node, if multiple exist, only the last one will take effect!)'
- );
- }
- if (!this._spFrame) {
- console.warn(
- IN_EDITOR_ZH
- ? '还未指定 [ 渲染纹理 ],拖入插件默认的 [ terrain-ground ] 贴图即可!'
- : ' (The [Terrain Texture] has not been specified yet, drag in the default [terrain-ground] texture of the plugin!)'
- );
- return;
- }
- if (!this._spEffectAsset) {
- console.warn(
- IN_EDITOR_ZH
- ? '还未指定 [ 渲染effect文件 ],拖入插件默认的 [ curvetexure-sprite.effect ] effect文件即可!'
- : ' (The [Effect Asset] has not been specified yet, drag in the default [curvetexure-sprite.effect] texture of the plugin!)'
- );
- return;
- }
- this._curve_height = this._spFrame.originalSize.height; //纹理高度 'Texture height'
- this._curve_width = this._spFrame.originalSize.width;
- //检查纹理的宽和高是否是2的指数 'Check if the width and height of the texture are powers of 2'
- if (this._curve_width & (this._curve_width - 1)) {
- console.warn(
- IN_EDITOR_ZH
- ? '纹理的宽度不是2的指数,请检查!'
- : ' (The width of the texture is not a power of 2, please check!)'
- );
- return;
- }
- if (this._curve_height & (this._curve_height - 1)) {
- console.warn(
- IN_EDITOR_ZH
- ? '纹理的高度不是2的指数,请检查!'
- : ' (The height of the texture is not a power of 2, please check!)'
- );
- return;
- }
- if (this.gizmoTag == null) {
- console.warn(
- IN_EDITOR_ZH
- ? '还未指定[ 控制点纹理 ]! 拖入 [ gizmotag ] 资源即可'
- : ' ([ Control Point Texture] has not been specified yet! Drag in the [gizmotag] resource)'
- );
- // return;
- }
- // if (this.gizmoTagLine == null) {
- // console.warn(
- // IN_EDITOR_ZH
- // ? '还未指定[ 控制点连接纹理 ]! 拖入 [ gizmotagline ] 资源即可'
- // : ' ([ Control Line Texture] has not been specified yet! Drag in the [gizmotagline] resource)'
- // );
- // // return;
- // }
- let c = this.node.getComponent(UIOpacity);
- if (c) {
- console.warn(
- this.node.name +
- (IN_EDITOR_ZH
- ? ' 请去除[ UIOpacity ]组件,实际渲染忽略这个属性,如果需要透明请修改原始贴图!'
- : ' (Please remove the [UIOpacity] component, this property is ignored in actual rendering, if transparency is needed, please modify the original texture!)')
- );
- }
- this._applySpriteFrame();
- if (this._syncOnOff && this._syncTarget) {
- //同步模式下不自身生成细分点 'In sync mode, do not generate subdivided points by itself'
- } else {
- this._vertexesSegment.length = 0;
- this._vertexesSegment = this._generateSegmentVertex(this._smoothness);
- this._updateHeadTail();
- if (this._headTailCloseOnoff && this.innerMeshSprite) {
- this.innerMeshSprite.node.setWorldPosition(this.node.worldPosition);
- this.innerMeshSprite.setVertexes(this._vertexesSegment);
- }
- }
- if (this._vertexesSegment.length > 0) {
- this._updateMesh();
- this.generatePhysicData();
- }
- }
- private _applySpriteFrame() {
- if (this._spFrame && this.renderer) {
- this._curve_height_result = this._thickness == 0 ? this._curve_height : this._thickness;
- this._curve_collider_thickness = this._physicsThickness == 0 ? this._curve_height : this._physicsThickness;
- let texture = this._spFrame.texture;
- texture.setWrapMode(Texture2D.WrapMode.REPEAT, Texture2D.WrapMode.REPEAT, Texture2D.WrapMode.REPEAT);
- let mat = this.renderer.getMaterialInstance(0) as Material;
- if (!mat || mat.effectAsset !== this._spEffectAsset) {
- mat = new Material();
- mat.initialize({
- effectAsset: this._spEffectAsset,
- defines: {
- USE_TEXTURE: true,
- },
- });
- this.renderer.setMaterialInstance(mat, 0);
- }
- mat.setProperty('mainTexture', texture, 0);
- const uv = this._spFrame.uv;
- const uv_x = uv[0]; // ul
- const uv_y = uv[1]; // vb
- const uv_w = uv[6] - uv[0]; // ur - ul
- const uv_h = uv[5] - uv[1]; // vt - vb
- const altlasUV = v4(uv_x, uv_y, uv_w, uv_h);
- // console.log(this.node.name + ' altlasUV', altlasUV);
- mat.setProperty('altlasUV', altlasUV, 0);
- } else {
- if (!this._spFrame) {
- let mat = this.renderer!.getMaterialInstance(0) as Material;
- mat.setProperty('mainTexture', null, 0);
- }
- }
- }
- private _getTangentPos(current: Vec3, next: Vec3, angleRad: number, thickness: number): Vec3 {
- const v1 = next
- .clone()
- .subtract(current)
- .normalize()
- .toVec2()
- .rotate(angleRad)
- .multiplyScalar(thickness)
- .add(current.toVec2());
- return v1.toVec3();
- }
- private _updateHeadTail() {
- this._updateHead();
- this._updateTail();
- }
- private _transfromPositonWhenScale(inpos: Vec3, targetNode: Node) {
- const localPos = inpos.clone();
- const worldMat = targetNode.getWorldMatrix();
- const worldPos = new Vec3();
- Vec3.transformMat4(worldPos, localPos, worldMat);
- const invParentMat = new Mat4();
- Mat4.invert(invParentMat, targetNode.getWorldMatrix());
- const newLocalPos = new Vec3();
- Vec3.transformMat4(newLocalPos, worldPos, invParentMat);
- return newLocalPos;
- }
- private _updateHead() {
- if (this._headTailFixedOnOff) {
- if (!this._headTailFrame) {
- console.warn(IN_EDITOR_ZH ? '请指定对应的首尾纹理' : ' (Please set the head/tail texture)');
- return;
- }
- let headNode = this.node.getChildByName('_headNode_');
- if (!headNode) {
- headNode = new Node('_headNode_');
- headNode.setParent(this.node);
- }
- headNode.setSiblingIndex(0);
- headNode.setPosition(0, 0, this.node.z);
- // headNode.setScale(this.node.scale.x, this.node.scale.y, 1);
- let meshR = headNode.getComponent(MeshRenderer);
- if (!meshR) {
- meshR = headNode.addComponent(MeshRenderer);
- }
- // let uiMeshR = this.node.getComponent(UIMeshRenderer);
- // if (!uiMeshR) {
- // headNode.getComponent(MeshRenderer)?.destroy();
- // } else {
- // if (!headNode.getComponent(UIMeshRenderer)) {
- // headNode.addComponent(UIMeshRenderer);
- // }
- // }
- let uiMeshR = headNode.getComponent(UIMeshRenderer);
- if (!uiMeshR) {
- headNode.addComponent(UIMeshRenderer);
- }
- let mat = meshR.getMaterialInstance(0) as Material;
- if (!mat) {
- mat = new Material();
- mat.initialize({
- effectAsset: this._spEffectAsset,
- defines: {
- USE_TEXTURE: true,
- },
- });
- meshR.setMaterialInstance(mat, 0);
- }
- mat.setProperty('mainTexture', this._headTailFrame.texture, 0);
- const uv = this._headTailFrame.uv;
- const uv_x = uv[0]; // ul
- const uv_y = uv[1]; // vb
- const uv_w = uv[6] - uv[0]; // ur - ul
- const uv_h = uv[5] - uv[1]; // vt - vb
- const altlasUV = v4(uv_x, uv_y, uv_w, uv_h);
- // console.log('altlasUV', altlasUV);
- mat.setProperty('altlasUV', altlasUV, 0);
- const cur = this._vertexesSegment[0]; //.clone().multiply(this.node.scale);
- const next = this._vertexesSegment[1]; //.clone().multiply(this.node.scale);
- let p2 = cur.clone();
- let p3 = this._getTangentPos(p2, next, -Math.PI / 2, this._curve_height_result);
- let p0 = cur.clone().subtract(next).normalize().multiplyScalar(this._headTailFrame.originalSize.width).add(cur);
- let p1 = p3.clone().subtract(p2).add(p0);
- if (this._renderMode === TerrainRenderMode.TANGENT_MODE_CENTER) {
- const vAdd: Vec3 = p0.clone().subtract(p1).multiplyScalar(0.5);
- p0 = p0.add(vAdd);
- p1 = p1.add(vAdd);
- p2 = p2.add(vAdd);
- p3 = p3.add(vAdd);
- }
- let posset: number[] = [];
- let tmp: Vec3[] = [];
- tmp.push(p0, p1, p2, p3);
- for (let i = 0; i < tmp.length; i++) {
- posset.push(tmp[i].x, tmp[i].y, tmp[i].z);
- }
- let uvs = [0, 0, 0, 1, 1, 0, 1, 1];
- let indices = this.node.scale.x * this.node.scale.y > 0 ? [0, 1, 2, 1, 3, 2] : [0, 2, 1, 1, 2, 3];
- // let indices = [0, 1, 2, 1, 3, 2];
- if (headNode.scale.x * headNode.scale.y < 0) {
- indices = [0, 2, 1, 1, 2, 3];
- }
- let mesh = utils.MeshUtils.createMesh({
- positions: posset,
- uvs: uvs,
- indices: indices,
- });
- if (meshR!.mesh) {
- meshR.mesh!.destroy();
- }
- meshR.mesh = mesh;
- } else {
- let headNode = this.node.getChildByName('_headNode_');
- if (headNode) {
- headNode.destroy();
- }
- }
- }
- private _updateTail() {
- if (this._headTailFixedOnOff) {
- if (!this._headTailFrame) {
- console.warn(IN_EDITOR_ZH ? '请指定对应的首尾纹理' : ' (Please set the head/tail texture)');
- return;
- }
- let tailNode = this.node.getChildByName('_tailNode_');
- if (!tailNode) {
- tailNode = new Node('_tailNode_');
- tailNode.setParent(this.node);
- }
- tailNode.setSiblingIndex(0);
- tailNode.setPosition(0, 0, this.node.z);
- // tailNode.setScale(this.node.scale.x, this.node.scale.y, 1);
- let meshR = tailNode.getComponent(MeshRenderer);
- if (!meshR) {
- meshR = tailNode.addComponent(MeshRenderer);
- }
- // let uiMeshR = this.node.getComponent(UIMeshRenderer);
- // if (!uiMeshR) {
- // tailNode.getComponent(MeshRenderer)?.destroy();
- // } else {
- // if (!tailNode.getComponent(UIMeshRenderer)) {
- // tailNode.addComponent(UIMeshRenderer);
- // }
- // }
- let uiMeshR = tailNode.getComponent(UIMeshRenderer);
- if (!uiMeshR) {
- tailNode.addComponent(UIMeshRenderer);
- }
- let mat = meshR.getMaterialInstance(0) as Material;
- if (!mat) {
- mat = new Material();
- mat.initialize({
- effectAsset: this._spEffectAsset,
- defines: {
- USE_TEXTURE: true,
- },
- });
- // this._headTailFrame.texture.setWrapMode(
- // Texture2D.WrapMode.REPEAT,
- // Texture2D.WrapMode.REPEAT,
- // Texture2D.WrapMode.REPEAT
- // );
- meshR.setMaterialInstance(mat, 0);
- }
- mat.setProperty('mainTexture', this._headTailFrame.texture, 0);
- const uv = this._headTailFrame.uv;
- const uv_x = uv[0]; // ul
- const uv_y = uv[1]; // vb
- const uv_w = uv[6] - uv[0]; // ur - ul
- const uv_h = uv[5] - uv[1]; // vt - vb
- const altlasUV = v4(uv_x, uv_y, uv_w, uv_h);
- // console.log('altlasUV', altlasUV);
- mat.setProperty('altlasUV', altlasUV, 0);
- let p0 = this._vertexesSegment[this._vertexesSegment.length - 1].clone();
- let p1 = this._getTangentPos(
- p0,
- this._vertexesSegment[this._vertexesSegment.length - 2],
- Math.PI / 2,
- this._curve_height_result
- );
- let p2 = p0
- .clone()
- .subtract(this._vertexesSegment[this._vertexesSegment.length - 2])
- .normalize()
- .multiplyScalar(this._headTailFrame.originalSize.width)
- .add(p0);
- let p3 = p2.clone().subtract(p0).add(p1);
- if (this._renderMode === TerrainRenderMode.TANGENT_MODE_CENTER) {
- const vAdd: Vec3 = p0.clone().subtract(p1).multiplyScalar(0.5);
- p0 = p0.add(vAdd);
- p1 = p1.add(vAdd);
- p2 = p2.add(vAdd);
- p3 = p3.add(vAdd);
- }
- let posset: number[] = [];
- let tmp: Vec3[] = [];
- tmp.push(p0, p1, p2, p3);
- for (let i = 0; i < tmp.length; i++) {
- posset.push(tmp[i].x, tmp[i].y, tmp[i].z);
- }
- let uvs = [1, 0, 1, 1, 0, 0, 0, 1];
- let indices = this.node.scale.x * this.node.scale.y > 0 ? [0, 1, 2, 1, 3, 2] : [0, 2, 1, 1, 2, 3];
- let mesh = utils.MeshUtils.createMesh({
- positions: posset,
- uvs: uvs,
- indices: indices,
- });
- if (meshR!.mesh) {
- meshR.mesh!.destroy();
- }
- meshR.mesh = mesh;
- } else {
- let headNode = this.node.getChildByName('_tailNode_');
- if (headNode) {
- headNode.destroy();
- }
- }
- }
- //细分控制点,通过Catmull-Rom 样条曲线来插值平滑 'Subdivide control points, interpolate and smooth using Catmull-Rom spline'
- private _catmullRom(p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, t: number): Vec3 {
- const t2 = t * t;
- const t3 = t2 * t;
- // Catmull-Rom spline formula for 3D
- const x =
- 0.5 *
- (2 * p1.x +
- (-p0.x + p2.x) * t +
- (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 +
- (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
- const y =
- 0.5 *
- (2 * p1.y +
- (-p0.y + p2.y) * t +
- (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
- (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
- // const z =
- // 0.5 *
- // (2 * p1.z +
- // (-p0.z + p2.z) * t +
- // (2 * p0.z - 5 * p1.z + 4 * p2.z - p3.z) * t2 +
- // (-p0.z + 3 * p1.z - 3 * p2.z + p3.z) * t3);
- return v3(x, y, 0);
- }
- //最小分段距离 'Minimum segment length'
- private _minSegmentLength: number = 8;
- private _generateSegmentVertex(seg_count: number = 8, physics: boolean = false): Vec3[] {
- const _seg: Vec3[] = [];
- // this._vertexesSegment.length = 0;
- const points = this._controlPoints;
- const pointCount = points.length;
- if (0 === seg_count) {
- _seg.push(...points);
- physics ||
- console.log(
- this.node.name + (IN_EDITOR_ZH ? ' 细分后点数量为:' : ' (Number of points after subdivision):'),
- _seg.length
- );
- if (this._headTailCloseOnoff) {
- _seg.push(points[0].clone());
- }
- return _seg;
- }
- if (pointCount < 2) {
- if (pointCount === 1) {
- _seg.push(points[0].clone());
- }
- return _seg;
- }
- if (pointCount === 2) {
- const p0 = points[0];
- const p1 = points[1];
- _seg.push(p0.clone());
- _seg.push(p1.clone());
- return _seg;
- }
- // Catmull-Rom样条 'Catmull-Rom spline'
- const paddedPoints: Vec3[] = [];
- let interCounts = 0;
- if (this._headTailCloseOnoff) {
- paddedPoints.push(points[points.length - 1].clone());
- paddedPoints.push(...points);
- paddedPoints.push(points[0].clone());
- paddedPoints.push(points[1].clone());
- interCounts = pointCount;
- } else {
- interCounts = pointCount - 1;
- paddedPoints.push(points[0].clone().add(points[0].clone().subtract(points[1])));
- paddedPoints.push(...points);
- paddedPoints.push(
- points[pointCount - 1].clone().add(points[pointCount - 1].clone().subtract(points[pointCount - 2]))
- );
- }
- for (let i = 0; i < interCounts; i++) {
- const p0 = paddedPoints[i];
- const p1 = paddedPoints[i + 1];
- const p2 = paddedPoints[i + 2];
- const p3 = paddedPoints[i + 3];
- if (i === 0) {
- _seg.push(p1.clone());
- }
- // 计算该段的长度(用p1-p2的距离近似) 'Calculate the length of this segment (approximated by the distance between p1 and p2)'
- const dist = p1.clone().subtract(p2).length();
- let seg = seg_count;
- // 如果距离小于最小分段距离,则不分段,直接连接
- if (dist < 40) {
- seg = 1;
- } else {
- while (seg > 1 && dist / seg < this._minSegmentLength) {
- seg = Math.floor(seg / 2);
- }
- }
- const step = 1 / seg;
- for (let j = 1; j <= seg; j++) {
- const t = j * step;
- const interpolatedPoint = this._catmullRom(p0, p1, p2, p3, t);
- _seg.push(interpolatedPoint);
- }
- }
- // if (this._vertexesSegment.length > 0) {
- // const lastOriginalPoint = points[pointCount - 1];
- // this._vertexesSegment[this._vertexesSegment.length - 1] = lastOriginalPoint.clone();
- // }
- // // 需要在不同模式下分别考虑
- // for (let i = this._vertexesSegment.length - 1; i > 0; i--) {
- // // if (this._vertexesSegment[i].equals(this._vertexesSegment[i - 1], 2)) {
- // if (isCloseToVec3(this._vertexesSegment[i], this._vertexesSegment[i - 1], 2)) {
- // this._vertexesSegment.splice(i, 1);
- // }
- // }
- // this._vertexesSegment = this._vertexesSegment.filter(
- // (v) => isFinite(v.x) && isFinite(v.y) && (v.x !== 0 || v.y !== 0)
- // );
- physics ||
- console.log(
- this.node.name + (IN_EDITOR_ZH ? ' 细分后点数量为:' : ' (Number of points after subdivision):'),
- _seg.length
- );
- return _seg;
- }
- private _updateMesh() {
- if (!this._spFrame || this._controlPoints.length < 2) {
- console.warn(IN_EDITOR_ZH ? '至少需要两个控制点' : 'Need at least 2 points to define the curve.');
- this.renderer!.mesh = null;
- return;
- }
- const positions = this._calculateVertexes();
- if (positions.length === 0) {
- console.warn(this.node.name, IN_EDITOR_ZH ? '顶点数据数量为0' : 'No vertexes calculated.');
- this.renderer!.mesh = null;
- return;
- }
- const uvs =
- this._renderUVMode == TerrainRenderUVMode.UV_REPEAT_GRID ? this._calculateUVs() : this._calculateTangentsUVs();
- // const uvs = this._calculateUVs();
- const indices = this._calculateIndices();
- // const normals = this._calculateNormals();
- // console.log('CurveTexture.ts _updateMesh pos ', positions);
- // console.log('CurveTexture.ts _updateMesh uvs ', uvs);
- // console.log('CurveTexture.ts _updateMesh indices ', indices);
- // console.log('CurveTexture.ts _updateMesh normals ', normals);
- let mesh = utils.MeshUtils.createMesh({
- positions: positions,
- uvs: uvs,
- indices: indices,
- // normals: normals,
- });
- // console.log('CurveTexture.ts _updateMesh positions ', positions);
- // console.log('CurveTexture.ts _updateMesh uvs ', uvs);
- // console.log('CurveTexture.ts _updateMesh indices ', indices);
- if (this.renderer!.mesh) {
- this.renderer!.mesh.destroy();
- }
- this.renderer!.mesh = mesh;
- }
- /**
- * 计算两条线段的交点
- * @param p1 第一条线段的起点
- * @param p2 第一条线段的终点
- * @param p3 第二条线段的起点
- * @param p4 第二条线段的终点
- * @param extended 是否考虑延长线,默认false
- * @returns 交点(Vec2),如果没有交点则返回 false
- */
- private _crossPointBy2Segment(
- p1: Vec2 | Vec3,
- p2: Vec2 | Vec3,
- p3: Vec2 | Vec3,
- p4: Vec2 | Vec3,
- extended: boolean = false
- ): Vec2 {
- const a = p1,
- b = p2; // 第一条线段: a->b
- const c = p3,
- d = p4; // 第二条线段: c->d
- // 计算分母 'Calculate denominator'
- const denominator = (d.y - c.y) * (b.x - a.x) - (d.x - c.x) * (b.y - a.y);
- if (denominator === 0) return null; // 平行或共线 'Parallel or collinear'
- // 计算 ua 和 ub 'Calculate ua and ub'
- const ua = ((d.x - c.x) * (a.y - c.y) - (d.y - c.y) * (a.x - c.x)) / denominator;
- const ub = ((b.x - a.x) * (a.y - c.y) - (b.y - a.y) * (a.x - c.x)) / denominator;
- if (extended) {
- return v2(a.x + ua * (b.x - a.x), a.y + ua * (b.y - a.y));
- }
- // 检查交点是否在线段上 'Check if the intersection point is on the segment'
- if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) {
- return v2(a.x + ua * (b.x - a.x), a.y + ua * (b.y - a.y));
- }
- return null;
- }
- /**
- * 计算从向量a到向量b的有符号夹角
- * @param a 起始向量
- * @param b 目标向量
- * @param normal 参考法向量(决定正方向),默认(0,0,1)
- * @returns 夹角(范围:-π ~ π),零向量返回NaN
- */
- private _signedAngle3D(a: Vec3, b: Vec3, normal?: Vec3): number {
- // 检查零向量
- const magA = Math.sqrt(a.x ** 2 + a.y ** 2 + a.z ** 2);
- const magB = Math.sqrt(b.x ** 2 + b.y ** 2 + b.z ** 2);
- if (magA === 0 || magB === 0) return NaN;
- // 计算点积和叉积
- const dot = a.x * b.x + a.y * b.y + a.z * b.z;
- const cross = {
- x: a.y * b.z - a.z * b.y,
- y: a.z * b.x - a.x * b.z,
- z: a.x * b.y - a.y * b.x,
- };
- const nor = normal ? normal : v3(0, 0, 1);
- // 叉积与法向量的点积决定方向
- const crossDotNormal = cross.x * nor.x + cross.y * nor.y + cross.z * nor.z;
- const sign = Math.sign(crossDotNormal);
- // 计算无符号夹角
- const cosTheta = Math.min(1, Math.max(-1, dot / (magA * magB)));
- const unsignedAngle = Math.acos(cosTheta);
- return sign * unsignedAngle;
- }
- //根据一组控制点和一个厚度,计算TANGENT_MODE模式下的顺时针90°顶点
- private _calclateTangentPoints(indata: Vec3[], thickness: number): Vec3[] {
- thickness = thickness < 0 ? 1 : thickness;
- let pCurrentClockLast = new Vec2(0, 0);
- let pNextClockLast = new Vec2(0, 0);
- let outPoints: Vec3[] = [];
- let temp = indata;
- if (this._headTailCloseOnoff) {
- temp = indata.map((v) => v.clone());
- temp.push(temp[1].clone());
- }
- for (let index = 0; index < temp.length; index++) {
- outPoints.push(temp[index].clone());
- //使用下半距离平行线交点方式来取点
- const thick = thickness * 1.0;
- let directVec = new Vec3(0, 0, 0);
- let directNormalVec = new Vec2(0, 0);
- if (index == temp.length - 1) {
- directVec = temp[index].clone().subtract(temp[index - 1]);
- directNormalVec = directVec.clone().normalize().toVec2();
- } else {
- directVec = temp[index + 1].clone().subtract(temp[index]);
- directNormalVec = directVec.clone().normalize().toVec2();
- }
- let clockwisePoint = directNormalVec
- .clone()
- .rotate(-Math.PI / 2)
- .multiplyScalar(thick);
- let pCurrentClock = temp[index].toVec2().add(clockwisePoint);
- if (index < temp.length - 1) {
- let pNextClock = temp[index + 1].toVec2().add(clockwisePoint);
- if (index > 0) {
- const lenStand = 1.414 * thick;
- //计算顺时针交点
- let crossPointClock = this._crossPointBy2Segment(
- pCurrentClockLast,
- pNextClockLast,
- pCurrentClock,
- pNextClock,
- true
- );
- if (crossPointClock) {
- const vDir1 = crossPointClock.clone().subtract(temp[index].toVec2());
- const vDirNormal1 = vDir1.clone().normalize();
- if (vDir1.length() > lenStand && this._smoothness > 6) {
- //以前后向量垂直模式下为长度标准
- crossPointClock = vDirNormal1.clone().multiplyScalar(lenStand).add(temp[index].toVec2());
- // console.log('crossPointClock 太远,截断重置', crossPointClock);
- }
- outPoints.push(crossPointClock.toVec3());
- } else {
- console.log(`${this.node.name} 交点计算失败,交点退化为原始点`);
- outPoints.push(pCurrentClock.toVec3());
- }
- }
- pNextClockLast = pNextClock;
- }
- if (index === 0 || index === temp.length - 1) {
- outPoints.push(pCurrentClock.toVec3());
- }
- pCurrentClockLast = pCurrentClock;
- }
- if (this._headTailCloseOnoff) {
- outPoints.pop();
- outPoints.pop();
- outPoints[0] = outPoints[outPoints.length - 2];
- outPoints[1] = outPoints[outPoints.length - 1];
- }
- //对一些异常情况处理,如果控制点和顺时针点的连线和任何一段控制点线段相交,则这个顺时针点修改为这个交点
- const outPointsVec2 = outPoints.map((v) => v.toVec2());
- for (let index = 0; index < outPointsVec2.length; index += 2) {
- if (0 === index || outPointsVec2.length - 2 === index) continue;
- const pCur = outPointsVec2[index];
- const pCurClock = outPointsVec2[index + 1];
- for (let j = 0; j < outPointsVec2.length; j += 2) {
- if (outPointsVec2.length - 2 === j) continue;
- if (j === index || j === index - 2 || j === index + 2) continue;
- const pSeg0 = outPointsVec2[j];
- const pSeg1 = outPointsVec2[j + 2];
- const r = this._crossPointBy2Segment(pCur, pCurClock, pSeg0, pSeg1, false);
- if (r) {
- outPoints[index + 1].x = r.x;
- outPoints[index + 1].y = r.y;
- // console.log(`检测到索引为${index}的点的顺时针顶点异常,已修正为交点`, r);
- }
- }
- }
- return outPoints;
- }
- //根据一组控制点和一个厚度,计算TANGENT_MODE_CENTER模式下的上下顶点
- private _calclateTangentCenterPoints(indata: Vec3[], thickness: number, physics: boolean = false): Vec3[] {
- thickness = thickness < 0 ? 1 : thickness;
- let pCurrentAnticlockLast = new Vec2(0, 0);
- let pCurrentClockLast = new Vec2(0, 0);
- let pNextAnticlockLast = new Vec2(0, 0);
- let pNextClockLast = new Vec2(0, 0);
- let outPoints: Vec3[] = [];
- let temp = indata;
- if (this._headTailCloseOnoff) {
- temp = indata.map((v) => v.clone());
- temp.push(temp[1].clone());
- }
- for (let index = 0; index < temp.length; index++) {
- //使用上下半距离平行线交点方式来取点
- const halfH = thickness * 0.5;
- let directVec = new Vec3(0, 0, 0);
- let directNormalVec = new Vec2(0, 0);
- if (index == temp.length - 1) {
- directVec = temp[index].clone().subtract(temp[index - 1]);
- directNormalVec = directVec.clone().normalize().toVec2();
- } else {
- directVec = temp[index + 1].clone().subtract(temp[index]);
- directNormalVec = directVec.clone().normalize().toVec2();
- }
- let antiClockwisePoint = directNormalVec
- .clone()
- .rotate(Math.PI / 2)
- .multiplyScalar(halfH);
- let clockwisePoint = directNormalVec
- .clone()
- .rotate(-Math.PI / 2)
- .multiplyScalar(halfH);
- let pCurrentAnticlock = temp[index].toVec2().add(antiClockwisePoint);
- let pCurrentClock = temp[index].toVec2().add(clockwisePoint);
- if (index < temp.length - 1) {
- let pNextAnticlock = temp[index + 1].toVec2().add(antiClockwisePoint);
- let pNextClock = temp[index + 1].toVec2().add(clockwisePoint);
- if (index > 0) {
- //计算逆时针交点
- let crossPointAnticlock = this._crossPointBy2Segment(
- pCurrentAnticlockLast,
- pNextAnticlockLast,
- pCurrentAnticlock,
- pNextAnticlock,
- true
- );
- const lenStand = 1.414 * halfH;
- if (crossPointAnticlock) {
- const vDir = crossPointAnticlock.clone().subtract(temp[index].toVec2());
- const vDirNormal = vDir.clone().normalize();
- if (vDir.length() > lenStand && this._smoothness > 6) {
- //以前后向量垂直模式下为长度标准
- crossPointAnticlock = vDirNormal.clone().multiplyScalar(lenStand).add(temp[index].toVec2());
- // console.log('crossPointAnticlock 太远,截断重置', crossPointAnticlock);
- }
- outPoints.push(crossPointAnticlock.toVec3());
- } else {
- console.log(`${this.node.name} 交点计算失败,交点退化为原始点`);
- outPoints.push(pCurrentAnticlock.toVec3());
- }
- //计算顺时针交点
- let crossPointClock = this._crossPointBy2Segment(
- pCurrentClockLast,
- pNextClockLast,
- pCurrentClock,
- pNextClock,
- true
- );
- if (crossPointClock) {
- const vDir1 = crossPointClock.clone().subtract(temp[index].toVec2());
- const vDirNormal1 = vDir1.clone().normalize();
- if (vDir1.length() > lenStand && this._smoothness > 6) {
- //以前后向量垂直模式下为长度标准
- crossPointClock = vDirNormal1.clone().multiplyScalar(lenStand).add(temp[index].toVec2());
- // console.log('crossPointClock 太远,截断重置', crossPointClock);
- }
- outPoints.push(crossPointClock.toVec3());
- } else {
- console.log(`${this.node.name} 交点计算失败,交点退化为原始点`);
- outPoints.push(pCurrentClock.toVec3());
- }
- }
- pNextAnticlockLast = pNextAnticlock;
- pNextClockLast = pNextClock;
- }
- if (index === 0) {
- outPoints.push(pCurrentAnticlock.toVec3());
- outPoints.push(pCurrentClock.toVec3());
- }
- if (index === temp.length - 1) {
- outPoints.push(pCurrentAnticlock.toVec3());
- outPoints.push(pCurrentClock.toVec3());
- }
- pCurrentAnticlockLast = pCurrentAnticlock;
- pCurrentClockLast = pCurrentClock;
- }
- if (this._headTailCloseOnoff) {
- outPoints.pop();
- outPoints.pop();
- outPoints[0] = outPoints[outPoints.length - 2];
- outPoints[1] = outPoints[outPoints.length - 1];
- }
- // if (physics) {
- // const evenPoints = outPoints.filter((_, idx) => idx % 2 === 0);
- // const end = evenPoints.pop();
- // console.log(`${this.node.name} evenPoints 1 :`, evenPoints);
- // this._openedInsectLinesWipe(evenPoints);
- // console.log(`${this.node.name} evenPoints 2 :`, evenPoints);
- // const tmpPoints: Vec3[] = [];
- // if (evenPoints.length > 12) {
- // tmpPoints.push(evenPoints[evenPoints.length - 3]);
- // tmpPoints.push(evenPoints[evenPoints.length - 2]);
- // tmpPoints.push(evenPoints[evenPoints.length - 1]);
- // tmpPoints.push(end);
- // tmpPoints.push(evenPoints[0]);
- // tmpPoints.push(evenPoints[1]);
- // tmpPoints.push(evenPoints[2]);
- // this._openedInsectLinesWipe(tmpPoints);
- // console.log(`${this.node.name} evenPoints 3 :`, evenPoints);
- // }
- // }
- return outPoints;
- }
- //处理一段不闭合的连续点的自相交
- // private _openedInsectLinesWipe(evenPoints: Vec2[] | Vec3[]) {
- // for (let i = 0; i < evenPoints.length; i++) {
- // const a = evenPoints[i];
- // const b = evenPoints[i + 1];
- // for (let j = i + 2; j < evenPoints.length - 1; j++) {
- // const c = evenPoints[j];
- // const d = evenPoints[j + 1];
- // const p = this._crossPointBy2Segment(a, b, c, d, false);
- // if (p) {
- // // 替换B到C区间的点为交点P
- // // for (let k = i + 1; k <= j; k++) {
- // // evenPoints[k].x = p.x;
- // // evenPoints[k].y = p.y;
- // // }
- // d.x = c.x;
- // d.y = c.y;
- // // 只处理一次交点,跳出
- // // break;
- // }
- // }
- // }
- // }
- private _calculateVertexes() {
- let positions: number[] = [];
- this._vertexes.length = 0;
- if (this._vertexesSegment.length < 2) {
- console.warn(this.node.name, IN_EDITOR_ZH ? '细分点数量小于2' : 'count of subdivided points is less than 2');
- return positions;
- }
- let temp: Vec3[] = [];
- for (let i = 0; i < this._vertexesSegment.length; i++) {
- const v = this._vertexesSegment[i];
- if (v) {
- temp.push(v.clone().add(this._offset));
- }
- }
- if (this._renderMode == TerrainRenderMode.VERTICAL_MODE) {
- for (let index = 0; index < temp.length - 1; index++) {
- const item1 = temp[index];
- const item2 = temp[index + 1];
- const item1_bottom = item1.clone().add(v3(0, -this._curve_height_result, 0));
- const item2_bottom = item2.clone().add(v3(0, -this._curve_height_result, 0));
- this._vertexes.push(item1);
- this._vertexes.push(item1_bottom);
- this._vertexes.push(item2);
- this._vertexes.push(item1_bottom);
- this._vertexes.push(item2_bottom);
- this._vertexes.push(item2);
- }
- } else if (this._renderMode == TerrainRenderMode.TANGENT_MODE) {
- this._vertexes = this._calclateTangentPoints(temp, this._curve_height_result);
- } else if (this._renderMode == TerrainRenderMode.TANGENT_MODE_CENTER) {
- this._vertexes = this._calclateTangentCenterPoints(temp, this._curve_height_result);
- }
- // console.log(this._vertexes);
- this._vertexes.forEach((item) => {
- positions.push(item.x, item.y, item.z);
- });
- return positions;
- }
- private _calculateUVs() {
- let uvs: number[] = [];
- if (!this._spFrame || this._vertexes.length === 0 || this._vertexesSegment.length < 2) {
- return uvs;
- }
- for (let i = 0; i < this._vertexes.length; i++) {
- const d = this._vertexes[i];
- const u = d.x / this._curve_width;
- let v = 1.0 - d.y / this._curve_height;
- if (this._updownFix) {
- v = d.y / this._curve_height;
- }
- uvs.push(u, v);
- }
- return uvs;
- }
- private _calculateTangentsUVs() {
- let uvs: number[] = [];
- const textureWidth = this._curve_width;
- const textureHeight = this._curve_height;
- const textureSegmentLength = textureWidth;
- let index = 0;
- let last_pos_up: Vec3 | null = null;
- let length_all_up = 0;
- let current_top_u = 0;
- for (const pt of this._vertexes) {
- let u = 0;
- let v = 0;
- if (index % 2 == 0) {
- v = this._updownFix ? this._curve_height_result / textureHeight : 0;
- if (last_pos_up != null) {
- length_all_up += pt.clone().subtract(last_pos_up).length();
- // 直接计算重复的纹理坐标
- u = length_all_up / textureSegmentLength;
- } else {
- u = 0;
- }
- current_top_u = u;
- last_pos_up = pt;
- } else {
- v = this._updownFix ? 0 : this._curve_height_result / textureHeight;
- u = current_top_u;
- }
- uvs.push(u, v);
- index += 1;
- }
- return uvs;
- }
- private _calculateNormals() {
- let normals: number[] = [];
- const normal = v3(0, 0, 1);
- for (let i = 0; i < this._vertexes.length; i++) {
- normals.push(normal.x, normal.y, normal.z);
- }
- return normals;
- }
- private _calculateIndices() {
- const indices: number[] = [];
- const vertexCount = this._vertexes.length;
- for (let i = 0; i < vertexCount - 2; i++) {
- if (this.node.scale.x * this.node.scale.y > 0) {
- indices.push(i);
- indices.push(i % 2 == 0 ? i + 1 : i + 2);
- indices.push(i % 2 == 0 ? i + 2 : i + 1);
- } else {
- indices.push(i);
- indices.push(i % 2 == 0 ? i + 2 : i + 1);
- indices.push(i % 2 == 0 ? i + 1 : i + 2);
- }
- }
- return indices;
- }
- //-------------------提取物理多边形点------------------------ 'Extract physical polygon points'
- private _extractPolygonOutline(indata: Vec3[]) {
- if (this._renderMode == TerrainRenderMode.VERTICAL_MODE) {
- const points: Vec2[] = [];
- let d = this._vertexesSegment.map((item) => item.clone().add(this._offsetCollider).toVec2());
- points.push(...d);
- let down = d.map((item) => item.clone().add(v2(0, -this._curve_collider_thickness))).reverse();
- points.push(...down);
- return points;
- } else if (this._renderMode == TerrainRenderMode.TANGENT_MODE_CENTER) {
- //切线模式2
- let d = this._vertexesSegment.map((item) => item.clone().add(this._offsetCollider));
- let verts = this._calclateTangentCenterPoints(d, this._curve_collider_thickness, true);
- //特殊处理闭合
- // if (this._headTailCloseOnoff) {
- //这里计算物理多边形的逻辑有点绕,需要先确认按照正常的细分点得到的结果是否自交,如果自交,丢弃,不处理(尤其是凹多边形的自交和裁剪是个大问题).
- //如果自交,则直接退化到细分为1 的结果进行计算,细分为1的结果确定不会自交.
- let inner = verts.filter((_, idx) => idx % 2 === 0);
- let smoothness = this._smoothness;
- while (UsefulTools.isPolygonSelfIntersect(inner).result && smoothness > 2) {
- smoothness -= 2;
- const _seg: Vec3[] = this._generateSegmentVertex(smoothness, true);
- verts = this._calclateTangentCenterPoints(_seg, this._curve_collider_thickness, true);
- inner = verts.filter((_, idx) => idx % 2 === 0);
- }
- // }
- let pointsAnticlock: Vec2[] = [];
- let pointsClock: Vec2[] = [];
- for (let i = 0; i < verts.length; i++) {
- if (i % 2 == 0) {
- pointsAnticlock.push(verts[i].clone().add(this._offsetCollider).toVec2());
- } else {
- pointsClock.push(verts[i].clone().add(this._offsetCollider).toVec2());
- }
- }
- return pointsAnticlock.concat(pointsClock.reverse());
- } else if (this._renderMode == TerrainRenderMode.TANGENT_MODE) {
- let d = this._vertexesSegment.map((item) => item.clone().add(this._offsetCollider));
- let verts = this._calclateTangentPoints(d, this._curve_collider_thickness);
- // if (this._headTailCloseOnoff) {
- let inner = verts.filter((_, idx) => idx % 2 === 0);
- let smoothness = this._smoothness;
- while (UsefulTools.isPolygonSelfIntersect(inner).result && smoothness > 2) {
- smoothness -= 2;
- const _seg: Vec3[] = this._generateSegmentVertex(smoothness, true);
- verts = this._calclateTangentCenterPoints(_seg, this._curve_collider_thickness, true);
- inner = verts.filter((_, idx) => idx % 2 === 0);
- }
- // }
- let pointsOrigin: Vec2[] = [];
- let pointsClock: Vec2[] = [];
- for (let i = 0; i < verts.length; i++) {
- if (i % 2 == 0) {
- pointsOrigin.push(verts[i].clone().add(this._offsetCollider).toVec2());
- } else {
- pointsClock.push(verts[i].clone().add(this._offsetCollider).toVec2());
- }
- }
- return pointsOrigin.concat(pointsClock.reverse());
- }
- }
- private _NAME_OF_PHYSICSNODE: string = 'physicContainerNode';
- //同步更新物理组件数据 'Synchronize and update physical component data'
- private generatePhysicData() {
- if (!this._physcisOnOff) {
- let pnode = this.node.getChildByName(this._NAME_OF_PHYSICSNODE);
- if (pnode) {
- pnode.destroy();
- }
- return;
- }
- //将物理组件挂在一个指定的子节点上,保证这个子节点为第一个.
- let pnode = this.node.getChildByName(this._NAME_OF_PHYSICSNODE);
- if (!pnode) {
- pnode = new Node(this._NAME_OF_PHYSICSNODE);
- pnode.setSiblingIndex(0);
- this.node.addChild(pnode);
- }
- // pnode.x = 0;
- // pnode.y = 0;
- pnode.setPosition(0, 0, 0);
- this._physicsCollider = pnode.getComponent(PolygonCollider2D);
- if (!this._physicsCollider) {
- this._physicsCollider = pnode.addComponent(PolygonCollider2D);
- }
- const rigid = pnode.getComponent(RigidBody2D);
- if (!rigid) {
- const rcomp = pnode.addComponent(RigidBody2D);
- rcomp.type = ERigidBody2DType.Static;
- }
- // const indices = this._calculateIndices();
- const polygonPoints = this._extractPolygonOutline(this._vertexes);
- this._physicsCollider.points = polygonPoints;
- this._physicsCollider.apply();
- }
- //---------------------- 编辑器中编辑功能--------------- 'Edit map in editor'
- @property({})
- private _visibleOnOff: boolean = true;
- @property({
- type: CCBoolean,
- tooltip: IN_EDITOR_ZH ? '控制点显示/隐藏' : ' (Whether to display gizmo in editor)',
- displayName: IN_EDITOR_ZH ? '控制点显示/隐藏' : ' (Whether to display gizmo in editor)',
- group: IN_EDITOR_ZH ? '控制点属性' : ' (Terrain Editing Parameter Control)',
- })
- get gizmoVisibleOnOff() {
- return this._visibleOnOff;
- }
- set gizmoVisibleOnOff(value) {
- this._editNodes.forEach((item) => {
- item.active = value;
- });
- this._visibleOnOff = value;
- }
- @property({})
- private _sortInXDirection: boolean = false;
- @property({
- type: CCBoolean,
- tooltip: IN_EDITOR_ZH ? '是否在X轴方向排序' : ' (Sort in X direction)',
- displayName: IN_EDITOR_ZH ? '是否在X轴方向排序' : ' (Sort in X direction)',
- group: IN_EDITOR_ZH ? '控制点属性' : ' (Terrain Editing Parameter Control)',
- })
- get sortInXDirection() {
- return this._sortInXDirection;
- }
- set sortInXDirection(value) {
- this._sortInXDirection = value;
- }
- @property({})
- private _editWhenRun: boolean = false;
- @property({
- type: CCBoolean,
- tooltip: IN_EDITOR_ZH ? '运行时存在/不存在' : 'editWhenRun',
- displayName: IN_EDITOR_ZH ? '运行时存在/不存在' : 'editWhenRun',
- group: IN_EDITOR_ZH ? '控制点属性' : ' (Terrain Editing Parameter Control)',
- })
- get editWhenRun() {
- return this._editWhenRun;
- }
- set editWhenRun(value) {
- this._editWhenRun = value;
- // this._refreshAll();
- }
- private _registerKeyForGizmo() {
- // if (!EDITOR) return;
- // input.on(Input.EventType.KEY_DOWN, (event: EventKeyboard) => {
- // if (event.keyCode === KeyCode.SPACE) {
- // this._showOnOff = false;
- // this.showOnOff = !this.showOnOff;
- // }
- // });
- }
- @property({ type: SpriteFrame })
- private _gizmotag: SpriteFrame | null = null;
- @property({
- type: SpriteFrame,
- tooltip: IN_EDITOR_ZH ? '控制点纹理' : ' (Texture of Terrain Control Points)',
- displayName: IN_EDITOR_ZH ? '控制点纹理' : ' (Texture of Terrain Control Points)',
- group: {
- name: IN_EDITOR_ZH ? '控制点属性' : ' (Terrain Editing Parameter Control)',
- style: GROUP_STYLE_NAME,
- },
- })
- get gizmoTag() {
- return this._gizmotag;
- }
- set gizmoTag(value) {
- this._gizmotag = value;
- // this._refreshAll();
- }
- @property({})
- private _gizmoColor: Color = new Color('#9EECFF');
- @property({
- type: Color,
- tooltip: IN_EDITOR_ZH
- ? '编辑控制点时的gizmo颜色,alpha值最小值为50'
- : ' (Gizmo color when editing control points, alpha value is >=50)',
- displayName: IN_EDITOR_ZH ? '控制点颜色' : ' (Color of Control Points)',
- group: IN_EDITOR_ZH ? '控制点属性' : ' (Points Editing Parameter Control)',
- })
- get gizmoColor() {
- return this._gizmoColor;
- }
- set gizmoColor(value) {
- let a = value.a < 50 ? 50 : value.a;
- a = value.a > 200 ? 200 : value.a;
- // console.log('gizmoColor', this._gizmoColor);
- this._gizmoColor.set(value.r, value.g, value.b, a);
- // this._refreshAll();
- }
- // @property({ type: SpriteFrame })
- // private _gizmotagline: SpriteFrame | null = null;
- // @property({
- // type: SpriteFrame,
- // tooltip: IN_EDITOR_ZH ? '控制点连接纹理' : ' (Texture of Control Lines)',
- // displayName: IN_EDITOR_ZH ? '控制点连接纹理' : ' (Texture of Control Lines)',
- // group: {
- // name: IN_EDITOR_ZH ? '控制点属性' : ' (Terrain Editing Parameter Control)',
- // style: GROUP_STYLE_NAME,
- // },
- // })
- // get gizmoTagLine() {
- // return this._gizmotagline;
- // }
- // set gizmoTagLine(value) {
- // this._gizmotagline = value;
- // // this._refreshAll();
- // }
- private _editNodes: Node[] = [];
- private _lastPositionHash: string = '';
- private _lastSyncPositionHash: string = '';
- private _lastEditNodes: Node[] = null;
- private _padStart(str: string, targetLength: number, padString: string = '0'): string {
- while (str.length < targetLength) {
- str = padString + str;
- }
- return str;
- }
- private _simpleHash(s: string, len: number = 8): string {
- let hash = 5381;
- for (let i = 0; i < s.length; i++) {
- hash = (hash << 5) + hash + s.charCodeAt(i);
- hash = hash & 0xffffffff;
- }
- let hex = (hash >>> 0).toString(16);
- return this._padStart(hex, len).slice(-len);
- }
- private _normalizeAngle(angle: number): number {
- return ((angle % 360) + 360) % 360; // 将角度转化为0-360之间的值 'Convert angle to a value between 0-360'
- }
- private _angleOfVec(vin: Vec2) {
- if (!vin) return 0;
- if (vin.x == 0 && vin.y == 0) return 0;
- let ve2 = v2(vin.x, vin.y);
- let a1 = misc.radiansToDegrees(ve2.signAngle(v2(1, 0)));
- return this._normalizeAngle(-a1);
- }
- //将AssetManager.instance.loadBundle('xxx');封装为一个同步函数,使用的时候可以await调用 'Encapsulate AssetManager.instance.loadBundle('xxx') as a synchronous function, can be called with await'
- // private async loadBundleSync(bundleName: string): Promise<AssetManager.Bundle> {
- // return new Promise((resolve, reject) => {
- // AssetManager.instance.loadBundle(bundleName, (err, bundle) => {
- // if (err) {
- // console.warn('loadBundleSync error: ', err);
- // reject(err);
- // } else {
- // resolve(bundle);
- // }
- // });
- // });
- // }
- // private async loadResSync(bundle: AssetManager.Bundle, path: string): Promise<Asset> {
- // return new Promise((resolve, reject) => {
- // bundle.load(path, (err, res) => {
- // if (err) {
- // console.warn('loadResSync error: ', err);
- // reject(err);
- // } else {
- // resolve(res);
- // }
- // });
- // });
- // }
- private _checkAndAddGizmoNodesByControlPoints() {
- const cpcount = this._controlPoints.length;
- const nodecount = this._editNodes.length;
- console.log(`cpcount`, cpcount);
- console.log(`nodecount`, nodecount);
- let mstart = 0;
- let mend = cpcount;
- for (let m = mstart; m < mend; m++) {
- // console.log('新添加了一个控制点');
- let pos = this._controlPoints[m];
- const n = new Node();
- n.name = 'gizmo_' + (m + 1);
- n.setPosition(pos.x, pos.y + 1, pos.z);
- const opa = n.addComponent(UIOpacity);
- let uit = n.addComponent(UITransform);
- const spc = n.addComponent(Sprite);
- this.node.addChild(n);
- spc.spriteFrame = this._gizmotag;
- uit.setContentSize(80, 80);
- const item = n;
- let sp = item.getComponent(Sprite);
- sp.color = this.gizmoColor;
- if (this._editWhenRun) {
- n.addComponent(MoveWithTouch);
- }
- }
- }
- protected async update(dt: number): Promise<void> {
- //跟随模式下统一处理数据 'Unified data processing in follow mode'
- if (this._syncOnOff && this._syncTarget) {
- const ct = this._syncTarget.getComponent(CurveTexture);
- this._controlPoints = ct!.controlPoints;
- this._vertexesSegment = ct!.vertexesSegment;
- this._updateHeadTail();
- this.node.setPosition(this._syncTarget.position.x, this._syncTarget.position.y);
- if (this.node.getScale().x !== this._syncTarget.scale.x || this.node.getScale().y !== this._syncTarget.scale.y) {
- this.node.setScale(this._syncTarget.scale.x, this._syncTarget.scale.y, 1);
- }
- // this.node.z = this._syncTarget.z;
- const parts: string[] = [];
- this._controlPoints.forEach((item) => parts.push(`${item.x},${item.y};`));
- this._vertexesSegment.forEach((item) => parts.push(`${item.x},${item.y};`));
- let str = parts.join('');
- str += `${this._sortInXDirection}`;
- let hash = this._simpleHash(str);
- //有数据变动才刷新 'Refresh only when there is data change'
- if (hash != this._lastSyncPositionHash) {
- this._lastSyncPositionHash = hash;
- this._refreshAll('1683');
- }
- //清除控制点 'Clear control points'
- this._editNodes = this.node.children.filter((item) => item.name.startsWith('gizmo_'));
- this._editNodes.forEach((item) => {
- item.destroy();
- });
- return;
- }
- if (this._gizmotag == null) {
- // console.warn('需要提前指定gizmo的资源! 拖入 gizmoTag 资源即可' +
- // ' (Gizmo resources need to be specified in advance! Drag in gizmoTag resources)');
- return;
- }
- // if (this._gizmotagline == null) {
- // return;
- // }
- //在编辑器中Ctrl+D,新增控制节点的时候
- if (EDITOR_NOT_IN_PREVIEW) {
- const t = this.node.children.filter((item) => item.name.startsWith('gizmo_'));
- if (this._lastEditNodes != null) {
- const newNodes = t
- .filter((node) => this._lastEditNodes.indexOf(node) === -1)
- .filter((node) => node.name.startsWith('gizmo_'));
- if (newNodes.length > 0) {
- //@ts-ignore
- // let curNodeUUID = await Editor.Selection.getLastSelected('node');
- // console.log(`curNodeUUID `, curNodeUUID);
- //@ts-ignore
- const curNodeUUIDs = await Editor.Selection.getSelected('node');
- // console.log(`curNodeUUIDs `, curNodeUUIDs);
- const curNodeUUID = curNodeUUIDs[curNodeUUIDs.length - 1];
- // 找到curNodeUUIDs中在_lastEditNodes出现的节点,取index最大的那个
- let maxIndex = -1;
- let curNode = null;
- let curNodeIndex = -1;
- for (const uuid of curNodeUUIDs) {
- const idx = this._lastEditNodes.findIndex((item) => item.uuid === uuid);
- if (idx > maxIndex) {
- maxIndex = idx;
- curNode = this._lastEditNodes[idx];
- curNodeIndex = idx;
- }
- }
- let preNode = this._lastEditNodes[curNodeIndex - 1];
- let nextNode = this._lastEditNodes[curNodeIndex + 1];
- let vdirNormal = v2(1, 0);
- if (nextNode) {
- // console.log(`nextNode 节点: ${nextNode.name}`);
- vdirNormal = nextNode.getPosition().clone().subtract(curNode.getPosition()).toVec2().normalize();
- } else if (preNode) {
- // console.log(`preNode 节点: ${preNode.name}`);
- vdirNormal = curNode.getPosition().clone().subtract(preNode.getPosition()).toVec2().normalize();
- }
- if (curNode) {
- let len = 150;
- if (nextNode) {
- len = nextNode.getPosition().clone().subtract(curNode.getPosition()).length();
- len = len / (newNodes.length + 1);
- }
- let sindex = curNode.getSiblingIndex();
- newNodes.forEach((item, index) => {
- // console.log(`设置 newNode : ${item.name}`);
- item.setSiblingIndex(sindex + 1); // siblingIndex会在每次设置后重新计算
- sindex = item.getSiblingIndex();
- const extenLen = len * (index + 1);
- item.setPosition(
- curNode
- .getPosition()
- .toVec2()
- .add(vdirNormal.clone().multiplyScalar(extenLen))
- .add(v2(0, 4 * (index + 1)))
- .toVec3()
- );
- });
- }
- }
- }
- this._lastEditNodes = t;
- }
- this._editNodes.length = 0;
- this._editNodes = this.node.children.filter((item) => item.name.startsWith('gizmo_'));
- if (!this._editWhenRun && !EDITOR_NOT_IN_PREVIEW) {
- this._editNodes.forEach((element) => {
- element.destroy();
- });
- return;
- }
- //初始自动添加编辑控制节点 'Initially automatically add editing control nodes'
- if (this._editNodes.length <= 0) {
- this._checkAndAddGizmoNodesByControlPoints();
- } else if (this._editNodes.length <= 1) {
- // this._checkAndAddGizmoNodesByControlPoints(false);
- this.scheduleOnce(() => {
- this._editNodes.length = 0;
- this._controlPoints.length = 0;
- this._controlPoints.push(v3(200, 200, 0));
- this._controlPoints.push(v3(400, 300, 0));
- }, 0);
- }
- //实时控制节点显示效果 'Real-time control node display effect'
- for (let i = 0; i < this._editNodes.length; i++) {
- const item = this._editNodes[i];
- let uit = item.getComponent(UITransform);
- uit.setContentSize(80, 80);
- let opa = item.getComponent(UIOpacity);
- opa.opacity = 255;
- let sp = item.getComponent(Sprite);
- sp.color = this.gizmoColor;
- sp.spriteFrame = this._gizmotag;
- if (this._editWhenRun) {
- if (item.getComponent(MoveWithTouch) == null) {
- item.addComponent(MoveWithTouch);
- }
- } else {
- const mtouch = item.getComponent(MoveWithTouch);
- if (mtouch) {
- mtouch.destroy();
- }
- }
- }
- if (this._sortInXDirection) {
- //将所有子节点按照X增序排列 'Sort all child nodes in ascending order of X'
- this._editNodes.sort((a, b) => {
- const posA = a.getPosition();
- const posB = b.getPosition();
- return posA.x - posB.x;
- });
- this._editNodes.forEach((item, index) => {
- item.setSiblingIndex(index + 10);
- });
- }
- // for (let i = 1; i < this._editNodes.length; i++) {
- // const pre = this._editNodes[i - 1];
- // const cur = this._editNodes[i];
- // const prePos = pre.getPosition();
- // const curPos = cur.getPosition();
- // if (prePos.x == curPos.x) {
- // cur.setPosition(curPos.x + 120, curPos.y, curPos.z);
- // }
- // }
- for (let j = 0; j < this._editNodes.length; j++) {
- const cur = this._editNodes[j];
- if (j == this._editNodes.length - 1) {
- if (this._headTailCloseOnoff) {
- } else {
- cur.angle = 0;
- break;
- }
- }
- let next = this._editNodes[j + 1];
- if (this._headTailCloseOnoff && j === this._editNodes.length - 1) {
- next = this._editNodes[0];
- }
- const curPos = cur.getPosition();
- const nextPos = next.getPosition();
- const ang = this._angleOfVec(nextPos.clone().subtract(curPos).toVec2());
- cur.angle = ang;
- }
- if (this._sortInXDirection) {
- //再次按照x增序排列 'Sort again in ascending order of x'
- this._editNodes.sort((a, b) => {
- const posA = a.getPosition();
- const posB = b.getPosition();
- return posA.x - posB.x;
- });
- this._editNodes.forEach((item, index) => {
- item.setSiblingIndex(index + 10);
- });
- }
- let str = '';
- this._editNodes.forEach((item) => {
- const pos = item.getPosition();
- str += `${pos.x},${pos.y},${pos.z};`;
- });
- let hash = this._simpleHash(str);
- if (hash !== this._lastPositionHash) {
- this._lastPositionHash = hash;
- this._controlPoints.length = 0;
- this._editNodes.forEach((item) => {
- const pos = item.getPosition();
- this._controlPoints.push(pos);
- });
- this._refreshAll('1567');
- }
- }
- }
|